package pprg;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import javax.imageio.ImageIO;

public class Mandelbrot {

	private static int width;
	private static int height;

	private static double x0;
	private static double y0;
	private static double x1;
	private static double y1;

	private static int iterations;

	private static int numberThreads = 0;

	/**
	 * max. runtime = 10 minutes
	 */
	private static long maxRuntime = 600L;

	private static BufferedImage img;

	public static void main(String[] args) throws InterruptedException {

		String img_name = "";
		if (args.length < 1) {
			System.err.println("Please specify image name as argument!");
			System.exit(0);
		} else {
			img_name = args[0];
			if (args.length == 2) {
				try {
					numberThreads = Integer.parseInt(args[1]);
				} catch (NumberFormatException e) {
					System.err.println("Enter number of threads as int");
				}
			}
		}

		width = Util.readIntInput("Enter width of image: ",
				"Please enter a int!");
		height = Util.readIntInput("Enter height of image: ",
				"Please enter a int!");

		x0 = Util.readDoubleInput("Enter x1 (left upper corner): ",
				"Please enter a double!");
		y0 = Util.readDoubleInput("Enter y1 (left upper corner): ",
				"Please enter a double!");
		x1 = Util.readDoubleInput("Enter x2 (right lower corner): ",
				"Please enter a double!");
		y1 = Util.readDoubleInput("Enter y2 (right lower corner): ",
				"Please enter a double!");

		iterations = Util.readIntInput("Enter iterations: ",
				"Please enter a int!");

		img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

		int numberProcessors = Runtime.getRuntime().availableProcessors();
		int poolsize;
		if (numberThreads != 0) {
			poolsize = numberThreads;
		} else {
			poolsize = numberProcessors * 4;
		}
		System.out.println("Number of Processors: " + numberProcessors
				+ " Number of Threads: " + poolsize);

		BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
		ThreadPoolExecutor pool = new ThreadPoolExecutor(poolsize, poolsize,
				maxRuntime, TimeUnit.SECONDS, queue);

		MandelBrotWorker job;
		MandelBrotWorker[] workers = new MandelBrotWorker[poolsize];
		int yStep = height / poolsize;
		long startTime = System.currentTimeMillis();
		int rest = height % poolsize;
		int numRows = yStep;

		for (int i = 0; i < poolsize; i++) {
			if (i == poolsize - 1)
				numRows = yStep + rest;
			job = new MandelBrotWorker(i * yStep, numRows, iterations, x0, y0,
					x1, y1, width, height);
			workers[i] = job;
			pool.execute(job);
		}

		pool.shutdown();
		pool.awaitTermination(maxRuntime, TimeUnit.SECONDS);

		long calcTime = System.currentTimeMillis();
		System.out.println("Time for calculation: " + (calcTime - startTime));

		// fill the image with the single parts which each worker created
		for (MandelBrotWorker worker : workers) {
			img.setRGB(0, worker.getStartRow(), width, worker.getNumRows(),
					worker.getRgbValues(), 0, width);
		}

		long endTime = System.currentTimeMillis();
		System.out.println("Total running time (calculation + filling image): "
				+ (endTime - startTime) + " ms");

		try {
			ImageIO.write(img, "png", new File(new Date().getTime() + "_"
					+ img_name));
		} catch (IOException e) {
			System.err.println("Unable to write image " + img_name + "!");
			e.printStackTrace();
		}
	}
}
